<?php
/**  
 * HTTP::Request  
 *  
 * Make requests via CURL. 
 *  
 * // Debugging
 * $mydebug = fopen('debug.txt','w');
 * curl_setopt($curl, CURLOPT_STDERR, $mydebug);
 * curl_setopt($curl, CURLOPT_VERBOSE, 1);
 *
 * @copyright  2009 Orb71.com  
 * @license    ?  
 * @version    Release: @package_version@  
 * @link       ?  
 */
class HTTP_Request {
    private $httpHeader = array();
    private $amzHeader  = array();
    private $curlopt    = array();

    public function __construct($args=array())
    {
        foreach($args as $prop => $val) {
            if(preg_match('/^CURLOPT_/',$prop)) {
                $this->curlopt[$prop] = $val;
            }
            else {
                $this->$prop = $val;
            }
        }

        $this->setHttpHeader('Expect','');
    }

    /**
     * Set http headers for requests. 
     *
     * @param string $key
     * @param string $val
     * @return null 
     */
    public function setHttpHeader($key, $val)
    {
        $key = trim($key);
        $val = trim($val);

        if(preg_match('/^x-amz/i',$key)) {
            $this->amzHeader[$key] = $val;
        }
        else {
            $this->httpHeader[$key] = $val;
        }
    }

    /**
     * Get the headers to send in request. 
     *
     * @return array
     */
    public function getHttpHeader($key=null)
    {
        $headers = array_merge($this->amzHeader,$this->httpHeader);
        if(isset($key)) {
            if(isset($headers[$key]))
                return $headers[$key];

            throw new Exception('Cannot find http request header: '.$key);
        }

        return $headers;
    }

    /**
     * This is primarily for the AWS signature.
     */
    public function getCanonAmz()
    {
        $amz=array();
        foreach($this->amzHeader as $key => $val) {
           $amz[] = strtolower($key).':'.trim($val);
        }
        sort($amz);
        return implode("\n",$amz);
    }

    /**
     * Glue together a URL.
     *
     * @param string $protocol
     * @param string $host
     * @param string $resource
     * @param array $query appropriate for php's http_build_query()
     * @return array
     */
    public function mkURL($protocol,$host,$resource="/",$query=array())
    {
        $url =  isset($protocol) ? $protocol : 'http://';
        $url .= $host;
        $url .= '/'.ltrim($resource,'/');
        
        if($query)
            $url .= $query ? '?'.http_build_query($query) : '';

        return $url;
    }

    /**
     * The $url can be a string or an array of urls to fetch.
     *
     * @param string $url
     * @return HTTP_Response
     */
    public function get($url)
    {
        $return_first_element = is_string($url) ? true : false;

        $url      = is_array($url) ? $url : array($url);
        $headers  = array();

        foreach($this->getHttpHeader() as $key => $val) {
            $headers[] = $key.': '.$val;
        }

        $mh       = curl_multi_init();
        $curl     = array();
        $response = array();
        for($i=0; $i<count($url); $i++) {
            $response[$i] = new HTTP_Response();

            $curl[$i] = curl_init();
            
            // Set curl ssl options
            if(preg_match('/^https/i',$url[$i])) {
                curl_setopt($curl[$i], CURLOPT_SSL_VERIFYHOST, 1);
                curl_setopt($curl[$i], CURLOPT_SSL_VERIFYPEER, 1);
            }
            
            if($this->curlopt) {
                foreach($this->curlopt as $key => $val) {
                    curl_setopt($curl[$i], constant($key), $val);
                }
            }
            
            curl_setopt($curl[$i], CURLOPT_RETURNTRANSFER, true);
            curl_setopt($curl[$i], CURLOPT_FOLLOWLOCATION, true);

            curl_setopt($curl[$i], CURLOPT_WRITEFUNCTION, array(&$response[$i], 'curlwritefunc'));
            curl_setopt($curl[$i], CURLOPT_HEADERFUNCTION, array(&$response[$i], 'curlheaderfunc'));

            curl_setopt($curl[$i], CURLOPT_HTTPHEADER, $headers);
            curl_setopt($curl[$i], CURLOPT_URL, $url[$i]);

            curl_multi_add_handle($mh,$curl[$i]);
        }

        // This might take a while.
        set_time_limit(0);

        $active = null;
        //execute the handles
        do {
            $mrc = curl_multi_exec($mh, $active);
        } while ($mrc == CURLM_CALL_MULTI_PERFORM);
        
        while ($active && $mrc == CURLM_OK) {
            if (curl_multi_select($mh) != -1) {
                do {
                    $mrc = curl_multi_exec($mh, $active);
                } while ($mrc == CURLM_CALL_MULTI_PERFORM);
            }
        }

        for($k=0; $k<count($curl); $k++) {
            $response[$k]->code = curl_getinfo($curl[$k], CURLINFO_HTTP_CODE);
            curl_multi_remove_handle($mh, $curl[$k]);
            curl_close($curl[$k]);
        }

        curl_multi_close($mh);

        return $return_first_element ? $response[0] : $response;
    }
    
    /**
     * post
     *
     * Post data to url. The post will handle one url or an
     * array of urls to execute.
     *
     * @param string $url
     * @param string $postdata
     * @return Response
     */
    public function post($url, $postdata)
    {
        $return_first_element = is_string($url) ? true : false;

        $url      = is_array($url) ? $url : array($url);
        $postdata = is_array($postdata) ? $postdata : array($postdata);

        $headers  = array();

        foreach($this->getHttpHeader() as $key => $val) {
            $headers[] = $key.': '.$val;
        }

        $mh       = curl_multi_init();
        $curl     = array();
        $response = array();
        for($i=0; $i<count($url); $i++) {
            $response[$i] = new HTTP_Response();

            $curl[$i] = curl_init();
            
            // Set curl ssl options
            if(preg_match('/^https/i',$url[$i])) {
                curl_setopt($curl[$i], CURLOPT_SSL_VERIFYHOST, 1);
                curl_setopt($curl[$i], CURLOPT_SSL_VERIFYPEER, 1);
            }

            if($this->curlopt) {
                foreach($this->curlopt as $key => $val) {
                    curl_setopt($curl[$i], constant($key), $val);
                }
            }

            curl_setopt($curl[$i], CURLOPT_RETURNTRANSFER, true);
            curl_setopt($curl[$i], CURLOPT_FOLLOWLOCATION, true);

            curl_setopt($curl[$i], CURLOPT_WRITEFUNCTION, array(&$response[$i], 'curlwritefunc'));
            curl_setopt($curl[$i], CURLOPT_HEADERFUNCTION, array(&$response[$i], 'curlheaderfunc'));

            curl_setopt($curl[$i], CURLOPT_HTTPHEADER, $headers);
            curl_setopt($curl[$i], CURLOPT_POST, 1);
            curl_setopt($curl[$i], CURLOPT_POSTFIELDS, $postdata[$i]);
            curl_setopt($curl[$i], CURLOPT_URL, $url[$i]);

            curl_multi_add_handle($mh,$curl[$i]);
        }

        // This might take a while.
        set_time_limit(0);

        $active = null;
        //execute the handles
        do {
            $mrc = curl_multi_exec($mh, $active);
        } while ($mrc == CURLM_CALL_MULTI_PERFORM);
        
        while ($active && $mrc == CURLM_OK) {
            if (curl_multi_select($mh) != -1) {
                do {
                    $mrc = curl_multi_exec($mh, $active);
                } while ($mrc == CURLM_CALL_MULTI_PERFORM);
            }
        }

        for($k=0; $k<count($curl); $k++) {
            $response[$k]->code = curl_getinfo($curl[$k], CURLINFO_HTTP_CODE);
            curl_multi_remove_handle($mh, $curl[$k]);
            curl_close($curl[$k]);
        }

        curl_multi_close($mh);

        return $return_first_element ? $response[0] : $response;
    }

    /**
     * fetch url then write it's data to a file.
     *
     * @param string $url
     * @param string $filename
     * @return Response
     */
    public function save($url,$filename)
    {
        $response = new HTTP_Response();
        $headers  = array();
        $secure   = preg_match('/^https/i',$url);

        foreach($this->getHttpHeader() as $key => $val) {
            $headers[] = $key.': '.$val;
        }

        $curl = curl_init();
        
        // Set curl ssl options
        if($secure) {
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1);
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1);
        }

        if($this->curlopt) {
            foreach($this->curlopt as $key => $val) {
                curl_setopt($curl[$i], constant($key), $val);
            }
        }

        // Get the file handle 
        if($fp = fopen($filename, 'w')) {
            curl_setopt($curl, CURLOPT_FILE, $fp);
        }
        else {
            throw new Exception("Could not open ".$filename); 
        }

        //curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$response, 'curlheaderfunc'));
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_URL, $url);

        // This might take a while.
        set_time_limit(0);

        if(curl_exec($curl)) {
            $response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        }

        curl_close($curl);

        return $response;
    }

    /**
     * getResponseToBrowser
     *
     * GET url. Send response directly to browser.
     *
     * @param string $url
     * @param string $postdata
     * @return null
     */
    public function getResponseToBrowser($url)
    {
        $response = new HTTP_Response();
        $headers  = array();
        $secure   = preg_match('/^https/i',$url);

        foreach($this->getHttpHeader() as $key => $val) {
            $headers[] = $key.': '.$val;
        }

        $curl = curl_init();
        
        // Set curl ssl options
        if($secure) {
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1);
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1);
        }


        if($this->curlopt) {
            foreach($this->curlopt as $key => $val) {
                curl_setopt($curl, constant($key), $val);
            }
        }

        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$response, 'curlheaderfunc'));
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_URL, $url);

        // Destroy any buffers because we are going directly to the browser
        while(@ob_end_clean());

        // This might take a while.
        set_time_limit(0);

        if(curl_exec($curl)) {
            $response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        }

        curl_close($curl);
    }

    /**
     * postResponseToBrowser
     *
     * Post data to url. The $postdata should look like Array.
     * Send response directly to browser
     *
     * @param string $url
     * @param string $postdata
     * @return null
     */
    public function postResponseToBrowser($url, $postdata=array())
    {
        $response = new HTTP_Response();
        $headers  = array();
        $secure   = preg_match('/^https/i',$url);

        foreach($this->getHttpHeader() as $key => $val) {
            $headers[] = $key.': '.$val;
        }

        $curl = curl_init();
        
        // Set curl ssl options
        if($secure) {
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1);
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1);
        }

        if($this->curlopt) {
            foreach($this->curlopt as $key => $val) {
                curl_setopt($curl, constant($key), $val);
            }
        }

        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$response, 'curlheaderfunc'));
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $postdata);
        curl_setopt($curl, CURLOPT_URL, $url);

        // Destroy any buffers because we are going directly to the browser
        while(@ob_end_clean());

        // This might take a while.
        set_time_limit(0);

        if(curl_exec($curl)) {
            $response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        }

        curl_close($curl);
    }

    /**
     * head
     *
     * @param string $url
     * @param boolean $followlocation
     */
    public function head($url,$followlocation=true)
    {
        $response = new HTTP_Response();
        $headers  = array();
        $secure   = preg_match('/^https/i',$url);

        foreach($this->getHttpHeader() as $key => $val) {
            $headers[] = $key.': '.$val;
        }

        $curl = curl_init();
        
        // Set curl ssl options
        if($secure) {
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1);
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1);
        }

        if($this->curlopt) {
            foreach($this->curlopt as $key => $val) {
                curl_setopt($curl, constant($key), $val);
            }
        }

        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD');
        curl_setopt($curl, CURLOPT_NOBODY, true);

        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, $followlocation);
        curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$response, 'curlwritefunc'));
        curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$response, 'curlheaderfunc'));
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_URL, $url);

        if(curl_exec($curl)) {
            $response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        }

        curl_close($curl);

        return $response;
    }

    public function put($url,$filename='')
    {
        $response = new HTTP_Response();
        $headers  = array();
        $secure   = preg_match('/^https/i',$url);

        foreach($this->getHttpHeader() as $key => $val) {
            $headers[] = $key.': '.$val;
        }

        $curl = curl_init();
        
        // Set curl ssl options
        if($secure) {
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1);
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1);
        }

        if($this->curlopt) {
            foreach($this->curlopt as $key => $val) {
                curl_setopt($curl, constant($key), $val);
            }
        }

        // This can be an object else bucket or copy put request 
        if($filename) {
            if($fp = fopen($filename, 'rb')) {
                curl_setopt($curl, CURLOPT_PUT, true);
                curl_setopt($curl, CURLOPT_INFILE, $fp);
                curl_setopt($curl, CURLOPT_INFILESIZE, filesize($filename));
            }
            else {
                throw new Exception("Could not open ".$filename); 
            }
        }
        else {
            curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT');
        }

        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$response, 'curlwritefunc'));
        curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$response, 'curlheaderfunc'));
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_URL, $url);

        if(curl_exec($curl)) {
            $response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        }

        curl_close($curl);

        if($filename)
            fclose($fp);

        return $response;
    }

    /**
     * Use this to create a webdav folder
     */
    public function mkCollection($url)
    {
        $response = new HTTP_Response();
        $headers  = array();
        $secure   = preg_match('/^https/i',$url);

        foreach($this->getHttpHeader() as $key => $val) {
            $headers[] = $key.': '.$val;
        }

        $curl = curl_init();
        
        // Set curl ssl options
        if($secure) {
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1);
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1);
        }

        if($this->curlopt) {
            foreach($this->curlopt as $key => $val) {
                curl_setopt($curl, constant($key), $val);
            }
        }

        // Send MKCOL
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'MKCOL');

        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$response, 'curlwritefunc'));
        curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$response, 'curlheaderfunc'));
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_URL, $url);

        if(curl_exec($curl)) {
            $response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        }

        curl_close($curl);

        return $response;
    }

    public function delete($url)
    {
        $response = new HTTP_Response();
        $headers  = array();
        $secure   = preg_match('/^https/i',$url);

        foreach($this->getHttpHeader() as $key => $val) {
            $headers[] = $key.': '.$val;
        }

        $curl = curl_init();
        
        // Set curl ssl options
        if($secure) {
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1);
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1);
        }

        if($this->curlopt) {
            foreach($this->curlopt as $key => $val) {
                curl_setopt($curl, constant($key), $val);
            }
        }

        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
        curl_setopt($curl, CURLOPT_NOBODY, true);

        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$response, 'curlwritefunc'));
        curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$response, 'curlheaderfunc'));
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_URL, $url);

        if(curl_exec($curl)) {
            $response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        }

        curl_close($curl);

        return $response;
    }
}
